<?php

namespace base\common\controller;

use app\admin\model\SystemAdmin;
use app\BaseController;
use app\common\constants\AdminConstant;
use app\common\service\AuthService;
use think\facade\Config;
use think\facade\Env;
use think\facade\View;
use think\Model;
use think\response\Json;
use think\template\exception\TemplateNotFoundException;
use think\Validate;

/**
 * Class AdminController.
 */
class AdminControllerBase extends BaseController
{
    use \app\common\traits\JumpTrait;

    protected $pageKeyName = null;

    /**
     * 当前模型.
     * @Model
     * @var object
     */
    protected $model;

    /**
     * 字段排序.
     * @var array
     */
    protected $sort = [
        'id' => 'desc',
    ];

    /**
     * 允许修改的字段.
     * @var array
     */
    protected $allowModifyFields = [
        'status',
        'sort',
        'remark',
        'is_delete',
        'title',
    ];

    /**
     * 不导出的字段信息.
     * @var array
     */
    protected $noExportFields = ['delete_time', 'update_time'];

    /**
     * 下拉选择条件.
     * @var array
     */
    protected $selectWhere = [];

    /**
     * 是否关联查询.
     * @var bool
     */
    protected $relationSearch = false;

    /**
     * 模板布局, false取消.
     * @var string|bool
     */
    protected $layout = 'layout/default';

    /**
     * 是否为演示环境.
     * @var bool
     */
    protected $isDemo = false;

    /**
     * 多元传参
     *
     * @var array
     */
    private $dataBrage = [];

    private $internalDataFields = [];

    protected $validateRule = null;

    protected $validateMessage = [];

    protected $batchValidate = false;

    /**
     * 导出文件的名称，支持中文，为空则获取模型名称.
     *
     * @var string
     */
    protected $exportFileName = null;

    /**
     * 当前登陆的管理员.
     * @var SystemAdmin
     */
    protected $sessionAdmin;

    /**
     * 权限服务
     *
     * @var AuthService
     */
    protected $authService;

    /**
     * 初始化方法.
     */
    protected function initialize()
    {
        if (empty($this->pageKeyName)) {
            $this->pageKeyName = $this->request->controller() . '_' . $this->request->action();
        }

        parent::initialize();
        $this->layout && $this->app->view->engine()->layout($this->layout);
        $this->isDemo = Env::get('adminsystem.is_demo', false);
        $this->checkAuth();
        $this->viewInit();

        $this->initSort();
        $this->assign('Controller', $this, -1);
    }

    public function initSort()
    {
        $sort = $this->request->param('sort');

        if (!empty($sort)) {
            $this->sort = $sort;
        }
    }

    /**
     * 模板变量赋值
     * @param string|array $name 模板变量
     * @param mixed $value 变量值
     * @return mixed
     */
    public function assign($name, $value = null, $isAppendToDataBrage = false, $forceObject = false)
    {
        if (is_array($name)) {
            $forceObject = $isAppendToDataBrage;
            $isAppendToDataBrage = $value;

            if ($isAppendToDataBrage === true) {
                foreach ($name as $k => $v) {
                    $this->setDataBrage($k, $v, $forceObject);
                }
            } elseif ($isAppendToDataBrage === -1) {
                $this->internalDataFields = array_merge($this->internalDataFields, array_keys($name));
            } else {
                foreach ($name as $k => $v) {
                    // 删除$this->internalDataFields中的数据
                    unset($this->internalDataFields[array_search($k, $this->internalDataFields)]);
                }
            }
        } else {
            if ($isAppendToDataBrage === true) {
                $this->setDataBrage($name, $value, $forceObject);
            } elseif ($isAppendToDataBrage === -1) {
                $this->internalDataFields[] = $name;
            } else {
                // 删除$this->internalDataFields中的数据
                unset($this->internalDataFields[array_search($name, $this->internalDataFields)]);
            }
        }

        return $this->app->view->assign($name, $value);
    }

    public function fetchJS($template = '')
    {
        $content_js = '';

        $common_template = '_common';

        if (!empty($template)) {
            $template_arr = explode('/', $template);
            unset($template_arr[count($template_arr) - 1]);
            $template_arr[] = '_common';
            $common_template = implode('/', $template_arr);
        }

        try {
            $content_js .= View::layout(false)->fetchJS($common_template);
            $content_js .= View::layout(false)->fetchJS($template);
        } catch (TemplateNotFoundException $th) {
            if (Env::get('adminsystem.strict_view_js', true)) {
                throw $th;
            }
        }

        return "<script>{$content_js}</script>";
    }

    /**
     * 解析和获取模板内容 用于输出.
     * @param string $template
     * @param array $vars
     * @return mixed
     */
    public function fetch($template = '', $vars = [])
    {
        $this->assign('data_brage', json_encode($this->dataBrage));
        $this->assign('content_js', $this->fetchJS($template), -1);

        $content_main = View::layout($this->layout)
            ->config([
                'view_suffix' => 'html',
            ])->fetch($template, $vars);

        $html = '';
        $html .= $content_main;
        if ($this->checkParseApi()) {
            $data = $this->parseInternalDataFields(View::fetchData());
            $r = json_message($data);

            return $r;
        }

        return $html;
    }

    public function checkParseApi()
    {
        if (Config::get('app.auto_parse_api')) {
            if ($this->request->isJson() && $this->request->param('get_page_data')) {
                return true;
            }
        }

        return false;
    }

    /**
     * 处理数据，过滤内部字段.
     * @param array $data 内部数据
     * @return array
     */
    public function parseInternalDataFields($data)
    {
        foreach ($this->internalDataFields as $field) {
            if (isset($data[$field])) {
                unset($data[$field]);
            }
        }

        if (isset($data['data_brage'])) {
            $data_brage = json_decode($data['data_brage'], true);

            // 如果data_brage中的key在data中存在，则删掉data_brage中的
            foreach ($data as $key => $value) {
                if (isset($data_brage[$key])) {
                    unset($data_brage[$key]);
                }
            }

            $data['data_brage'] = $data_brage;
        }

        return $data;
    }

    /**
     * 设置dataBrage数据.
     *
     * @param string $name 键
     * @param mixed $value 值
     * @param bool $forceObject 强制转为对象
     * @return void
     */
    public function setDataBrage($name, $value, $forceObject = false)
    {
        if (is_array($name)) {
            foreach ($name as $k => $v) {
                $this->setDataBrage($k, $v, $forceObject);
            }

            return $this;
        }

        if ($forceObject && !is_object($value)) {
            $value = (object) $value;
        }

        $this->dataBrage[$name] = $value;

        return $this;
    }

    /**
     * 重写验证规则.
     * @param array $data
     * @param array|string $validate
     * @param array $message
     * @param bool|null $batch
     * @return array|bool|string|true
     */
    public function validate(array $data, $validate, array $message = [], bool $batch = null)
    {
        try {
            $message = array_merge($this->validateMessage, $message);

            if (is_null($batch)) {
                $batch = $this->batchValidate;
            }

            if (!empty($validate)) {
                parent::validate($data, $validate, $message, $batch);
            } elseif (!empty($this->validateRule)) {
                if ($this->validateRule instanceof Validate) {
                    $this->validateRule->message($message);

                    // 是否批量验证
                    if ($batch) {
                        $this->validateRule->batch(true);
                    }

                    $this->validateRule->failException(true)->check($data);
                } elseif (is_array($this->validateRule)) {
                    parent::validate($data, $this->validateRule, $message, $batch);
                }
            }
        } catch (\Exception $e) {
            $this->error($e->getMessage());
        }

        return true;
    }

    /**
     * 构建请求参数.
     * @param array $excludeFields 忽略构建搜索的字段
     * @return array
     */
    protected function buildTableParames($excludeFields = [])
    {
        $get = $this->request->get('', null);

        $page = isset($get['page']) && !empty($get['page']) ? $get['page'] : 1;
        $limit = isset($get['limit']) && !empty($get['limit']) ? $get['limit'] : 15;
        $group = isset($get['group']) && !empty($get['group']) ? $get['group'] : null;
        $filters = isset($get['filter']) && !empty($get['filter']) ? $get['filter'] : '{}';
        $ops = isset($get['op']) && !empty($get['op']) ? $get['op'] : '{}';
        // json转数组
        $filters = json_decode($filters, true) ?? [];
        $ops = json_decode($ops, true);
        $where = [];
        $excludes = [];

        $request_options = [];

        // 判断是否关联查询
        $tableName = \think\helper\Str::snake(lcfirst($this->model->getName()));

        foreach ($filters as $key => $val) {
            if (in_array($key, $excludeFields)) {
                $excludes[$key] = $val;
                continue;
            }

            $op = isset($ops[$key]) && !empty($ops[$key]) ? $ops[$key] : '%*%';

            if (strpos($key, '[') === 0) {
                $key = str_replace('[', '', $key);

                $key = explode(']', $key)[0];
            }

            if ($this->relationSearch && count(explode('.', $key)) == 1) {
                $key = "{$tableName}.{$key}";
            }
            switch (strtolower($op)) {
                case '=':
                    $where[] = [$key, '=', $val];
                    break;
                case '%*%':
                    $where[] = [$key, 'LIKE', "%{$val}%"];
                    break;
                case '*%':
                    $where[] = [$key, 'LIKE', "{$val}%"];
                    break;
                case '%*':
                    $where[] = [$key, 'LIKE', "%{$val}"];
                case 'in':
                    $where[] = [$key, 'IN', "{$val}"];
                    break;
                case 'min':
                    $where[] = [$key, '>=', $val];
                    break;
                case 'max':
                    $where[] = [$key, '<=', $val];
                    break;
                case 'min_date':
                    $where[] = [$key, '>=', strtotime($val)];
                    break;
                case 'max_date':
                    $where[] = [$key, '<=', strtotime($val)];
                    break;
                case 'range':
                    [$beginTime, $endTime] = explode(' - ', $val);
                    $where[] = [$key, '>=', strtotime($beginTime)];
                    $where[] = [$key, '<=', strtotime($endTime)];
                    break;
                case 'none':
                    $request_options[$key] = $val;
                    break;
                default:
                    $where[] = [$key, $op, "%{$val}"];
            }
        }

        return [$page, $limit, $where, $excludes, $request_options, $group];
    }

    /**
     * 下拉选择列表.
     * @return Json
     */
    public function selectList()
    {
        $fields = input('selectFields');
        $data = $this->model
            ->where($this->selectWhere)
            ->field($fields)
            ->select();
        $this->success(null, $data);
    }

    /**
     * 初始化视图参数.
     */
    private function viewInit()
    {
        $request = app()->request;
        list($thisModule, $thisController, $thisAction) = [app('http')->getName(), app()->request->controller(), $request->action()];
        list($thisControllerArr, $jsPath) = [explode('.', $thisController), null];
        foreach ($thisControllerArr as $vo) {
            empty($jsPath) ? $jsPath = parse_name($vo) : $jsPath .= '/' . parse_name($vo);
        }
        $autoloadJs = file_exists(root_path('public') . "static/{$thisModule}/js/{$jsPath}.js") ? true : false;
        $thisControllerJsPath = "{$thisModule}/js/{$jsPath}.js";
        $adminModuleName = config('app.admin_alias_name');
        $isSuperAdmin = $this->sessionAdmin?->id == AdminConstant::SUPER_ADMIN_ID ? true : false;
        $data = [
            'adminModuleName' => $adminModuleName,
            'thisController' => parse_name($thisController),
            'thisAction' => $thisAction,
            'thisRequest' => parse_name("{$thisModule}/{$thisController}/{$thisAction}"),
            'thisControllerJsPath' => "{$thisControllerJsPath}",
            'autoloadJs' => $autoloadJs,
            'isSuperAdmin' => $isSuperAdmin,
            'version' => env('app_debug') ? time() : sysconfig('site', 'site_version'),
            'pageKeyName' => $this->pageKeyName,
        ];

        $this->assign($data, -1);
    }

    /**
     * 检测权限.
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    protected function checkAuth($currentNode = null, $haltRequest = true)
    {
        $adminConfig = config('admin');
        $admin = get_session_admin();

        $back_url = $this->request->url();

        $adminId = $admin['id'] ?? '';
        $expireTime = $admin['expire_time'] ?? '';

        /** @var AuthService $authService */
        $authService = app(AuthService::class, ['adminId' => $adminId]);
        $this->authService = $authService;

        $currentNode = $authService->getCurrentNode();
        if (is_null($currentNode)) {
            $currentNode = $authService->getCurrentNode();
        }

        $currentController = parse_name(app()->request->controller());

        // 验证登录
        if (
            !in_array($currentController, $adminConfig['no_login_controller']) &&
            !in_array($currentNode, $adminConfig['no_login_node'])
        ) {
            empty($adminId) && $this->error('请先登录后台', [], __url('admin/login/index', ['back_url' => $back_url]));

            // 判断是否登录过期
            if ($expireTime !== true && time() > $expireTime) {
                session('admin', null);
                $this->error('登录已过期，请重新登录', [], __url('admin/login/index', ['back_url' => $back_url]));
            }
        }

        // 验证权限
        if (
            !in_array($currentController, $adminConfig['no_auth_controller']) &&
            !in_array($currentNode, $adminConfig['no_auth_node'])
        ) {
            $check = $authService->checkNode($currentNode);

            if ($haltRequest) {
                if (!$check) {
                    $this->error('无权限访问');
                }
            } else {
                return $check;
            }

            // 判断是否为演示环境
            if (env('adminsystem.is_demo', false) && app()->request->isPost()) {
                $this->error('演示环境下不允许修改');
            }
        }

        $model_admin = SystemAdmin::autoCache('read', $adminId)->find($adminId);

        $this->sessionAdmin = $model_admin;

        $this->assign('session_admin', $model_admin, -1);
    }

    protected function readHeaderToken()
    {
        return read_header_token();
    }

    /**
     * 严格校验接口是否为POST请求
     */
    protected function checkPostRequest()
    {
        if (!$this->request->isPost()) {
            $this->error('当前请求不合法！');
        }
    }
}
